emacs markdown 代码块的快速选择
这个markdown模式和之前一篇org代码块快速选择类似。也是模仿了vim那样按下 Ctrl+c S 键就进入一个选择模式,然后按下 j k 可以用来选择上下文不同的代码块,按 y或者enter 选择复制文本,按 q 退出。
我在其中加个小改变,就是添加一段判断当前是否在emacs client打开当前,然后决定是否添加一段映射到markdown-quick-select-save-selection-to-file的代码。
(when (and (boundp 'server-buffer-clients) server-buffer-clients) (define-key markdown-quick-select-temp-keymap (kbd "s") 'markdown-quick-select-save-selection-to-file) )
这是我的个人需求,emacs本身已经有org了,markdown对很多用emacs的人来说,只能是org的简易版。所以我的markdown经常是在emacs ”外部“的打开,也就是在终端中emacsclient打开md文件。
通过添加这个代码,我就可以在用emacsclient的时候直接用s快捷键,把md中的code block提取出来保存在当前目录中,用来快速使用md中的代码。这个功能也可以帮助我快速从llm生成的markdown文件中拷贝代码。
;;;;; markdown-quick-select-mode (defvar markdown-quick-select-temp-keymap nil) (defvar markdown-quick-select-restrict-mode nil) ; 用来标记是否启用了限制模式 (defvar markdown-quick-select-original-keymap nil) ; 保存原始的 keymap (defun markdown-quick-select-toggle-restrict-keys () "切换限制按键的状态,按下 C-c S 时启用或关闭限制模式,仅在 markdown-mode 中生效。" (interactive) (if (not (derived-mode-p 'markdown-mode)) (message "此功能仅在 markdown-mode 中可用。") (if markdown-quick-select-restrict-mode (markdown-quick-select-enable-all-keys) ; 如果限制模式已启用,按下时解除限制 (markdown-quick-select-disable-other-keys)))) ; 否则启用限制模式 (defun markdown-quick-select-copy-code-block () "拷贝当前代码块到剪贴板。" (interactive) (let ((code-block-bounds (markdown-get-code-block-bounds))) (if code-block-bounds (let ((start (car code-block-bounds)) (end (cdr code-block-bounds))) (kill-ring-save start end) (message "已拷贝当前代码块")) (message "未找到代码块。")))) (defun markdown-quick-select-disable-other-keys () "屏蔽所有其他按键,仅允许 j/k 控制光标上下移动,并禁用输入,仅在 markdown-mode 中生效。" (interactive) (if (not (derived-mode-p 'markdown-mode)) (message "此功能仅在 markdown-mode 中可用。") ;; 保存当前的 keymap 到 markdown-quick-select-original-keymap (setq markdown-quick-select-original-keymap (current-local-map)) ;; 创建一个临时的 keymap,继承自原始的 markdown-mode keymap (setq markdown-quick-select-temp-keymap (let ((map (make-sparse-keymap))) (set-keymap-parent map markdown-mode-map) map)) ;; 只允许 'j', 'k' 控制上下移动 (define-key markdown-quick-select-temp-keymap (kbd "j") 'markdown-select-src-block-content-next) (define-key markdown-quick-select-temp-keymap (kbd "k") 'markdown-select-src-block-content-prev) ;; 将 M-w、y、Enter 键映射为复制当前代码块 (define-key markdown-quick-select-temp-keymap (kbd "M-w") 'markdown-quick-select-copy-code-block) (define-key markdown-quick-select-temp-keymap (kbd "y") 'markdown-quick-select-copy-code-block) (define-key markdown-quick-select-temp-keymap (kbd "RET") 'markdown-quick-select-copy-code-block) (define-key markdown-quick-select-temp-keymap (kbd "q") 'markdown-quick-select-toggle-restrict-keys) (when (and (boundp 'server-buffer-clients) server-buffer-clients) (define-key markdown-quick-select-temp-keymap (kbd "s") 'markdown-quick-select-save-selection-to-file)) ;; 使用临时 keymap (use-local-map markdown-quick-select-temp-keymap) ;; 启用只读模式,禁用其他输入 (setq buffer-read-only t) (setq markdown-quick-select-restrict-mode t) ;; 设置标记为限制模式已启用 (message "键盘被限制,只有 j/k 可用,按 C-c S 解除限制。"))) (defun markdown-get-code-block-bounds () "获取当前 Markdown 代码块的起始和结束位置。" (save-excursion (let ((start (save-excursion (if (re-search-backward "```" nil t) (progn (forward-line 1) (point)) nil))) (end (save-excursion (if (re-search-forward "```" nil t) (point-at-bol) nil)))) (if (and start end) (cons start end) nil)))) (defun markdown-select-src-block-content-next () "搜索下一个 code block,跳转到位置并全选内容,排除代码块标记。" (interactive) (let ((start (point))) ;; 搜索下一个代码块开始 (if (re-search-forward "```" nil t) (progn ;; 跳过语言标记行 (forward-line 1) ;; 搜索代码块结束 (let ((end (save-excursion (re-search-forward "```" nil t) (point-at-bol)))) (push-mark (point) t t) (goto-char end) (activate-mark))) ;; 如果没有找到下一个 code block,则给出提示 (message "没有找到下一个 code block")))) (defun markdown-select-src-block-content-prev () "搜索上一个 code block,跳转到位置并全选内容,排除代码块标记。" (interactive) (let ((start (point))) ;; 搜索上一个代码块结束 (if (re-search-backward "```" nil t) (progn ;; 向前搜索对应的代码块开始 (if (re-search-backward "```" nil t) (progn ;; 跳过语言标记行 (forward-line 1) (let ((begin (point)) (end (save-excursion (re-search-forward "```" nil t) (point-at-bol)))) (push-mark (point) t t) (goto-char end) (activate-mark))) (message "没有找到对应的代码块开始")) ) ;; 如果没有找到上一个 code block,则给出提示 (message "没有找到上一个 code block")))) (defun markdown-select-src-block-content () "选中当前 Markdown 模式中的代码块内容,不包括代码块标记。" (interactive) (save-excursion (let ((begin (save-excursion (re-search-backward "```" nil t) (forward-line 1) (point))) (end (save-excursion (re-search-forward "```" nil t) (point-at-bol)))) (goto-char begin) (set-mark (point)) (goto-char end) (activate-mark)))) (defun markdown-quick-select-save-selection-to-file (filename) "将当前选中的内容保存到指定的文件,文件名通过用户输入获得。" (interactive "F保存文件: ") (if (use-region-p) (let ((region-text (buffer-substring-no-properties (region-beginning) (region-end)))) (with-temp-file filename (insert region-text)) (message "选中的内容已保存到 %s" filename)) (message "没有选中的内容可保存。"))) (defun markdown-quick-select-enable-all-keys () "恢复所有按键并解除输入限制,仅在 markdown-mode 中生效。" (interactive) (if (not (derived-mode-p 'markdown-mode)) (message "此功能仅在 markdown-mode 中可用。") ;; 恢复原始的 keymap (use-local-map markdown-quick-select-original-keymap) ;; 解除只读模式,允许输入 (setq buffer-read-only nil) (setq markdown-quick-select-restrict-mode nil) ;; 设置标记为限制模式已解除 (message "已恢复所有按键和输入功能。")))